home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.10 Oct 95 / SlimApp / SlimApp ƒ / SlimApp.c next >
Encoding:
C/C++ Source or Header  |  1995-03-17  |  28.0 KB  |  879 lines  |  [TEXT/MMCC]

  1. /*==========================================================================
  2.  
  3.                                 SlimApp.c
  4.                                 
  5.     This file contains functions to determine if the running application    
  6.     is a fat application and if so, to strip the unneeded code from the
  7.     application.
  8.     
  9.     Add this file to your project and include "SlimApp.h" in the file(s)
  10.     that call the stripping routines.
  11.         
  12.     Copyright 1995 by Blake Ward.
  13.  
  14.     Permission is granted for unrestricted use, provided the author
  15.     is credited.
  16.         
  17.   ==========================================================================*/
  18.  
  19. #include "SlimApp.h"
  20.  
  21.  
  22.  
  23.  
  24. /*============================================================================
  25.   MISC STRUCTURES
  26.   ============================================================================*/
  27.  
  28. // This should have been defined by the UniversalHeaders for us, but I couldn't
  29. // find it.  Getting at the long version message is actually a pain since the
  30. // strings are packed and the short version string is of variable length.
  31. typedef struct versResource {
  32.     Byte versionNum1;
  33.     Byte versionNum2;
  34.     Byte release;
  35.     Byte stage;
  36.     short int regionCode;
  37.     Str255 versionNumber;        // Packed string, so next field has to be computed
  38.     //Str255 versionMessage;
  39.     } VersResource, **VersHandle;
  40.  
  41.  
  42.  
  43.  
  44. /*============================================================================
  45.   INTERNAL PROTOTYPES (Functions used only within this file)
  46.   ============================================================================*/
  47.  
  48. OSErr StripUnneededCode(short int appResFork, short int appVRefNum,
  49.                 short int appDirID, StringPtr appFileName);
  50.  
  51. void RenameSlimApplication(short int vRefNum, short int dirID, StringPtr name);
  52.  
  53. void ForceFinderToUpdateFolder(short int vRefNum, short int dirID);
  54.  
  55.  
  56.  
  57.  
  58.  
  59. /*============================================================================
  60.   Has68KPowerPCCode:
  61.  
  62.       Returns:
  63.           'k68KApplication' if this application contains only 68K code
  64.           'kPowerPCApplication' if it contains only PowerPC code
  65.           'kFatBinaryApplication' if it contains both.
  66.       
  67.       This function assumes a simple model of 68k/PowerPC applications:
  68.        - all of the PowerPC code is stored in the data fork
  69.        - if there's PPC code, there will be at least one 'cfrg' resource
  70.        - if there's 68K code, then our replacement stub will still be stored
  71.          under a different resource type (stripping the 68K code moves it over
  72.          to replace the applications regular 68K code resources).
  73.  
  74.       If you are using the Code Fragment Manager with 68K code, then you'll have
  75.       to make this function a little more sophisticated.
  76.  
  77.   ============================================================================*/
  78. OSErr
  79. Has68KPowerPCCode(void)
  80. {
  81.     OSErr err;
  82.     short int currResFork, applicationResourceFork;
  83.     Boolean is68KApp, isPowerPCApp;
  84.     
  85.     // If they didn't set this up for us, then we can't do much of anything
  86.     applicationResourceFork = GetApplicationResourceFork();
  87.  
  88.     // Keep track of the current resource fork so that we can restore everything
  89.     // to its previous state when we're done
  90.     currResFork = CurResFile();
  91.  
  92.     // Look for the code and cfrg resources in the application's resource fork.
  93.     UseResFile(applicationResourceFork);
  94.     
  95.     // First, see if our replacement 68K stub code resources are still stored
  96.     // under a different resource type.  .
  97.     if (Count1Resources(kStubCODEType) == 2)
  98.         is68KApp = true;
  99.     else is68KApp = false;
  100.  
  101.     if ((err = ResError()) != noErr) {
  102.         UseResFile(currResFork);
  103.         return err;
  104.         }
  105.  
  106.     // Also see if there are any 'cfrg' resources in the application
  107.     if (Count1Resources('cfrg') > 0)
  108.         isPowerPCApp = true;
  109.     else isPowerPCApp = false;
  110.  
  111.     err = ResError();
  112.     UseResFile(currResFork);
  113.     if (err != noErr)
  114.         return err;
  115.  
  116.     // Return the appropriate constant.
  117.     if (is68KApp && isPowerPCApp)
  118.         return kFatBinaryApplication;
  119.     else if (isPowerPCApp)
  120.             return kPowerPCApplication;
  121.          else return k68KApplication;
  122.  
  123. }
  124.  
  125.  
  126.  
  127.  
  128. /*============================================================================
  129.   SafeToStrip:
  130.   
  131.       This function attempts to figure out whether it will be safe/possible to
  132.       strip unneeded code from the application file at this time.
  133.       
  134.       Returns false if:
  135.           the application file is locked
  136.           the application file came from a remote server volume
  137.           the application file is on a locked volume
  138.  
  139.     If the file isn't currently shared by any other users, it would be possible
  140.     to strip it even if it resides on a remote server, but since it's a shared
  141.     copy, that would probably only cause problems later when another user with
  142.     a different machine tried to use it.
  143.     
  144.     Note that this routine allows the application to be stripped if it's on
  145.     a local volume and that volume is currently shared using FileSharing.
  146.     FileSharing won't let two users run the application at the same time, so
  147.     we're safe from stripping it out from under another user.  Generally a
  148.     user that happens to have volume/directory containing the application
  149.     will be sharing it so that others can copy files, not run applications,
  150.     so this seems like a reasonable situation to allow.
  151.       
  152.   ============================================================================*/
  153. Boolean
  154. SafeToStrip(void)
  155. {
  156.     OSErr err;
  157.     short int applicationResourceFork;
  158.     
  159.     FCBPBRec fcbParams;
  160.     Str63 appFileName;
  161.  
  162.     HParamBlockRec params;
  163.     CInfoPBRec pb;
  164.     GetVolParmsInfoBuffer volParms;
  165.     
  166.     // If they didn't set this up for us, then we can't do much of anything
  167.     applicationResourceFork = GetApplicationResourceFork();
  168.  
  169.     // Build a parameter block for an FCB info request.
  170.     fcbParams.ioCompletion = nil;
  171.     fcbParams.ioNamePtr = appFileName;
  172.     fcbParams.ioFCBIndx = 0;
  173.     fcbParams.ioRefNum = applicationResourceFork;
  174.     
  175.     // We shouldn't get an error retrieving the info.  If we do, there's
  176.     // probably something seriously wrong, so the last thing we want to
  177.     // do is say that it's safe to try to strip the application.
  178.     err = PBGetFCBInfo(&fcbParams, false);
  179.     if (err != noErr)
  180.         return false;
  181.  
  182.     // First, check to see if the volume that contains the application is
  183.     // currently locked.  If so, we won't be able to change the application.
  184.     // We get the volume's vRefNum from the values returned by the FCB call.
  185.     params.volumeParam.ioCompletion = nil;
  186.     params.volumeParam.ioVRefNum = fcbParams.ioFCBVRefNum;
  187.     params.volumeParam.ioVolIndex = 0;
  188.     params.volumeParam.ioNamePtr = nil;
  189.     err = PBHGetVInfo(¶ms, false);
  190.  
  191.     // Check the volume hardware and software locked bits
  192.     if (err != noErr ||
  193.         (params.volumeParam.ioVAtrb & 0x0080) != 0 ||
  194.         (params.volumeParam.ioVAtrb & 0x8000) != 0)
  195.         return false;
  196.  
  197.     // Is the file itself locked?  The application doesn't start out locked,
  198.     // so this would only happen if the user locked the file.  In any case,
  199.     // if they did then we should honor it.
  200.     pb.hFileInfo.ioNamePtr = appFileName;
  201.     pb.hFileInfo.ioVRefNum = fcbParams.ioFCBVRefNum;
  202.     pb.hFileInfo.ioDirID = fcbParams.ioFCBParID;
  203.     pb.hFileInfo.ioFDirIndex = 0;
  204.     err = PBGetCatInfoSync(&pb);
  205.     
  206.     // Check file locked bit
  207.     if (err != noErr || (pb.hFileInfo.ioFlAttrib & 0x01) != 0)
  208.         return false;
  209.  
  210.     // Get some general volume information to help us figure out whether we're
  211.     // running from a local volume or from a server.
  212.     params.ioParam.ioCompletion = nil;
  213.     params.ioParam.ioVRefNum = fcbParams.ioFCBVRefNum;
  214.     params.ioParam.ioNamePtr = nil;
  215.     params.ioParam.ioBuffer = (Ptr)&volParms;
  216.     params.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
  217.     err = PBHGetVolParms(¶ms, false);
  218.     if (err != noErr)
  219.         return false;
  220.  
  221.     // If it's a local volume, then there won't be any server address
  222.     if (volParms.vMServerAdr == 0)
  223.         return true;
  224.  
  225.     return false;
  226.     
  227. }
  228.  
  229.  
  230.  
  231.  
  232. /*============================================================================
  233.   StripFatApplication:
  234.       If running on a PowerPC, this function strips all the 68K code resources.
  235.       If running on a 68K, it strips out the PowerPC code stored in the data
  236.       fork.
  237.     
  238.     This function relies on the trick that we conditionally compile code for
  239.     it depending on whether we're generating the code for a 68K or PowerPC
  240.     processor.  For a fat binary, this file will get compiled twice and the
  241.     two results combined.  If we're executing the code generated for one
  242.     processor, we can be sure that the code for the other processor isn't
  243.     currently executing (and isn't needed) and we can safely delete it.
  244.       
  245.       This function assumes a simple model of 68k/PowerPC applications:
  246.        - all of the PowerPC code is stored in the data fork
  247.        - if there's PPC code, there will be at least one 'cfrg' resource
  248.        - if there's 68K code, then our replacement stub will still be stored
  249.          under a different resource type (stripping the 68K code moves it over
  250.          to replace the applications regular 68K code resources).
  251.  
  252.   ============================================================================*/
  253. OSErr
  254. StripFatApplication(void)
  255. {
  256.  
  257.     OSErr err;
  258.     short int currResFork, applicationResourceFork;
  259.  
  260.     FCBPBRec fcbParams;
  261.     Str63 appFileName;
  262.  
  263.     // If they didn't set this up for us, then we can't do much of anything
  264.     applicationResourceFork = GetApplicationResourceFork();
  265.  
  266.     // Keep track of the current resource fork so that we can restore everything
  267.     // to its previous state when we're done
  268.     currResFork = CurResFile();
  269.     
  270.     // We're also going to need to the file itself before we're done.  We can
  271.     // get it from the resource fork refNum without having to depend on the
  272.     // Process Manager
  273.     fcbParams.ioCompletion = nil;
  274.     fcbParams.ioNamePtr = appFileName;
  275.     fcbParams.ioFCBIndx = 0;
  276.     fcbParams.ioRefNum = applicationResourceFork;
  277.     
  278.     // We shouldn't get an error retrieving the info.  If we do, there's
  279.     // probably something seriously wrong and we're not going to be able
  280.     // to strip the application.
  281.     err = PBGetFCBInfo(&fcbParams, false);
  282.     if (err != noErr)
  283.         return err;
  284.     
  285.     // Look for the code and cfrg resources in the application's resource fork.
  286.     UseResFile(applicationResourceFork);
  287.  
  288.     err = StripUnneededCode(applicationResourceFork, fcbParams.ioFCBVRefNum, fcbParams.ioFCBParID, appFileName);
  289.  
  290.     UseResFile(currResFork);
  291.     
  292.     // Provided we successfully stripped the unneeded code, we want to try to
  293.     // change the application's name and it's long version string so that
  294.     // the user can tell months from now which version he/she has.
  295.     if (err == noErr)
  296.         RenameSlimApplication(fcbParams.ioFCBVRefNum, fcbParams.ioFCBParID, appFileName);
  297.     
  298.     return err;
  299.  
  300. }
  301.  
  302.  
  303.  
  304. #ifdef powerc    // The following version of this function will only be compiled
  305.                 // into the PowerPC version of the application
  306.                 
  307.  
  308. /*============================================================================
  309.   StripUnneededCode:        (Strip Unneeded 68K Code)
  310.   
  311.       This function relies on the trick that we can conditionally compile code
  312.       depending on whether we're generating the code for a 68K or PowerPC
  313.     processor.  This version of StripUnneededCode will only be compiled into
  314.     the PowerPC version of the application.  Therefore, if this code is
  315.     actually executing, we can be absolutely sure that it's safe to remove
  316.     the 68K version of the application.
  317.  
  318.     This function assumes that all of the 68K code is stored in 'CODE'
  319.     resources in the application's resource fork. It removes all of the
  320.     'CODE' resources and (if there is one) the 'DATA' resource that contains
  321.     initialization values for 68K global variables.
  322.     
  323.     In order to leave the application so that it will still run long enough
  324.     to warn the user if ever moved to a 68K machine, this function inserts a
  325.     tiny 68K application of its own.  This application does nothing but
  326.     initialize the toolbox, put up a warning dialog, and then quit.  You
  327.     should edit the dialog resource and customize it for your application
  328.     and company name.
  329.  
  330.     If you also have fat resources for WDEF's, CDEF's, etc. and you want
  331.     to strip them too, then you'll have to handle that yourself.
  332.  
  333.   ============================================================================*/
  334. OSErr
  335. StripUnneededCode(short int appResFork, short int /*appVRefNum*/, short int /*appDirID*/,
  336.                                 StringPtr /*appFileName*/)
  337. {
  338.  
  339.     OSErr err;
  340.     short int n;
  341.     Handle resourceHandle;
  342.  
  343.     // First, see how many 'code' resources are in the application
  344.     n = Count1Resources('CODE');
  345.     if ((err = ResError()) != noErr)
  346.         return err;
  347.     
  348.     // We don't actually want to read these resources into memory, we just want to
  349.     // get a reference to them so that we can remove them from the resource fork.
  350.     SetResLoad(false);
  351.  
  352.     // Delete all of the 'CODE' resources.
  353.     for (; n > 0; n--) {
  354.         resourceHandle = Get1IndResource('CODE', 1);
  355.         if ((err = ResError()) != noErr || resourceHandle == nil) {
  356.             SetResLoad(true);
  357.             return err;
  358.             }
  359.  
  360.         // The code resources start out protected, so we have to clear the
  361.         // protected flag before they can be removed
  362.         SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  363.  
  364.         RemoveResource(resourceHandle);
  365.         if ((err = ResError()) != noErr) {
  366.             SetResLoad(true);
  367.             return err;
  368.             }
  369.         DisposeHandle(resourceHandle);
  370.         }
  371.     
  372.     // Do the same for the DATA resource if it exists
  373.     resourceHandle = Get1Resource('DATA', 0);
  374.     if ((err = ResError()) == noErr && resourceHandle) {
  375.         // In case it's protected, clear that flag first
  376.         SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  377.  
  378.         RemoveResource(resourceHandle);
  379.         if ((err = ResError()) != noErr) {
  380.             SetResLoad(true);
  381.             return err;
  382.             }
  383.         DisposeHandle(resourceHandle);
  384.         }
  385.  
  386.     // We're finished deleting from the resource fork, so we want to make sure that
  387.     // this flag gets reset.  Forgetting to reset this would soon result in a crash.
  388.     SetResLoad(true);
  389.     
  390.     // OK, now we want to move our tiny 68K stub into place so that this application
  391.     // will still run long enough to warn the user if ever moved to a 68K machine
  392.     // It consists of two code resources and a new DATA resource that we stored
  393.     // under different resource types in the application.
  394.     resourceHandle = Get1Resource(kStubCODEType, kStubCodeID);
  395.     if ((err = ResError()) != noErr || resourceHandle == nil)
  396.         return err;    
  397.     SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  398.     RemoveResource(resourceHandle);
  399.     if ((err = ResError()) != noErr)
  400.         return err;
  401.     AddResource(resourceHandle,'CODE',0,"\p");
  402.     if ((err = ResError()) != noErr)
  403.         return err;
  404.     WriteResource(resourceHandle);
  405.     if ((err = ResError()) != noErr)
  406.         return err;
  407.     ReleaseResource(resourceHandle);
  408.  
  409.     // Move the second code resource
  410.     resourceHandle = Get1Resource(kStubCODEType, kStubCodeID + 1);
  411.     if ((err = ResError()) != noErr || resourceHandle == nil)
  412.         return err;
  413.     SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  414.     RemoveResource(resourceHandle);
  415.     if ((err = ResError()) != noErr)
  416.         return err;
  417.     AddResource(resourceHandle,'CODE',1,"\p");
  418.     if ((err = ResError()) != noErr)
  419.         return err;
  420.     WriteResource(resourceHandle);
  421.     if ((err = ResError()) != noErr)
  422.         return err;
  423.     ReleaseResource(resourceHandle);
  424.  
  425.     // Move our DATA resource that goes with the code resources we just moved
  426.     resourceHandle = Get1Resource(kStubDATAType, kStubDataID);
  427.     if ((err = ResError()) != noErr || resourceHandle == nil)
  428.         return err;
  429.     SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  430.     RemoveResource(resourceHandle);
  431.     if ((err = ResError()) != noErr)
  432.         return err;
  433.     AddResource(resourceHandle,'DATA',0,"\p");
  434.     if ((err = ResError()) != noErr)
  435.         return err;
  436.     WriteResource(resourceHandle);
  437.     if ((err = ResError()) != noErr)
  438.         return err;
  439.     ReleaseResource(resourceHandle);
  440.  
  441.     // Write all of the changes
  442.     UpdateResFile(appResFork);
  443.     if ((err = ResError()) != noErr)
  444.         return err;
  445.  
  446.     return noErr;
  447.  
  448. }
  449.  
  450.  
  451. #else        // The following version gets compiled into the 68K version
  452.             // of the application.
  453.  
  454.  
  455. /*============================================================================
  456.   StripUnneededCode:            (Strip PowerPC Code)
  457.  
  458.       This function relies on the trick that we can conditionally compile code
  459.       depending on whether we're generating the code for a 68K or PowerPC
  460.     processor.  This version of StripUnneededCode will only be compiled into
  461.     the 680x0 version of the application.  Therefore, if this code is
  462.     actually executing, we can be absolutely sure that it's safe to remove
  463.     the PowerPC version of the application.
  464.  
  465.     This function assumes that all of the PowerPC code is stored in the
  466.     application's data fork.
  467.     
  468.     It also assumes that all of the 'cfrg' resources in the resource fork are
  469.     part of the PowerPC version of the application and can safely be removed
  470.     with the data fork.  If you're also using the Code Fragment Manager with
  471.     some 68K code (probably a very unusual situation), then you'll have to
  472.     modify this function to be more selective about which 'cfrg' resources
  473.     it removes.
  474.  
  475.   ============================================================================*/
  476. OSErr
  477. StripUnneededCode(short int appResFork, short int appVRefNum, short int appDirID,
  478.                                 StringPtr appFileName)
  479. {
  480.  
  481.     OSErr err;
  482.     short int n, refNum;
  483.     Handle resourceHandle;
  484.  
  485.     // First, remove any 'cfrg' resources in the application resource fork
  486.     // If we don't get rid of these and someone runs the application on a
  487.     // PowerPC, the finder will think there's native PowerPC code available
  488.     // and won't emulate the 68K version.
  489.     n = Count1Resources('cfrg');
  490.     if ((err = ResError()) != noErr)
  491.         return err;
  492.  
  493.     // We don't actually want to read these resources into memory, we just want to
  494.     // get a reference to them so that we can remove them from the resource fork.
  495.     SetResLoad(false);
  496.  
  497.     // Delete all of the 'cfrg' resources.
  498.     for (; n > 0; n--) {
  499.         resourceHandle = Get1IndResource('cfrg', n);
  500.         if ((err = ResError()) == noErr && resourceHandle) {
  501.             RemoveResource(resourceHandle);
  502.             if ((err = ResError()) != noErr) {
  503.                 SetResLoad(true);
  504.                 return err;
  505.                 }
  506.             DisposeHandle(resourceHandle);
  507.             }
  508.         else {
  509.             SetResLoad(true);
  510.             return err;
  511.             }
  512.         }
  513.  
  514.     // Since we've just stripped the PowerPC version of the application, we know that
  515.     // we'll never be able to strip the 68K version, so there's no need to keep
  516.     // around the stub code.  Therefore, we'll make the app a little smaller by
  517.     // removing it too.
  518.     resourceHandle = Get1Resource(kStubCODEType, kStubCodeID);
  519.     if ((err = ResError()) == noErr && resourceHandle) {
  520.         // In case it's protected, clear that flag first
  521.         SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  522.         RemoveResource(resourceHandle);
  523.         if ((err = ResError()) != noErr) {
  524.             SetResLoad(true);
  525.             return err;
  526.             }
  527.         DisposeHandle(resourceHandle);
  528.         }
  529.     resourceHandle = Get1Resource(kStubCODEType, kStubCodeID + 1);
  530.     if ((err = ResError()) == noErr && resourceHandle) {
  531.         // In case it's protected, clear that flag first
  532.         SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  533.         RemoveResource(resourceHandle);
  534.         if ((err = ResError()) != noErr) {
  535.             SetResLoad(true);
  536.             return err;
  537.             }
  538.         DisposeHandle(resourceHandle);
  539.         }
  540.     resourceHandle = Get1Resource(kStubDATAType, kStubDataID);
  541.     if ((err = ResError()) == noErr && resourceHandle) {
  542.         // In case it's protected, clear that flag first
  543.         SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
  544.         RemoveResource(resourceHandle);
  545.         if ((err = ResError()) != noErr) {
  546.             SetResLoad(true);
  547.             return err;
  548.             }
  549.         DisposeHandle(resourceHandle);
  550.         }
  551.  
  552.     // We're finished with the resource fork, so we want to make sure that this flag
  553.     // gets reset.  Forgetting to reset this would soon result in a crash.
  554.     SetResLoad(true);
  555.  
  556.     // Write the changes
  557.     UpdateResFile(appResFork);
  558.     if ((err = ResError()) != noErr)
  559.         return(err);
  560.  
  561.     // Now we have to remove the actual PowerPC code.  This function assumes
  562.     // that it's all stored in the data fork of the application.  If your
  563.     // application has fat resources (e.g. WDEF functions) and you also want
  564.     // to strip the PowerPC code from them, you'll have to handle that
  565.     // specially here.
  566.  
  567.     // Open the data fork (which contains all of the PPC code)
  568.     err = HOpen(appVRefNum, appDirID, appFileName, fsRdWrPerm, &refNum);
  569.     if (err != noErr)
  570.         return(err);
  571.  
  572.     // And eliminate the whole data fork
  573.     err = SetEOF(refNum, 0);
  574.     if (err != noErr)
  575.         return err;
  576.  
  577.     err = FSClose(refNum);    
  578.     if (err != noErr)
  579.         return err;
  580.  
  581.     return noErr;
  582.  
  583. }
  584.  
  585. #endif
  586.  
  587.  
  588.  
  589.  
  590. /*============================================================================
  591.   RenameSlimApplication:
  592.  
  593.       This routine updates the name of the application and its version string.
  594.       
  595.       If the application's file name ends the kFatBinaryFileName string
  596.       which is currently set to " (Fat)" in the STR# resource, then this routine
  597.       renames the file to remove it.
  598.       
  599.       If the long version string for the application contains the string
  600.       kFatBinaryVersionName, this routine replaces it with kPowerPCVersionName or
  601.       k68KVersionName as appropriate.
  602.  
  603.   ============================================================================*/
  604. void
  605. RenameSlimApplication(short int vRefNum, short int dirID, StringPtr name)
  606. {
  607.     Str63 newName;
  608.     Str255 searchStr, replaceStr;
  609.     Handle theStrHandle;
  610.  
  611.     // Regardless of which version of the code we're stripping, if the filename
  612.     // ends in " (Fat)" then we should remove that:
  613.     
  614.     // Get the string we're searching for
  615.     GetIndString(searchStr, kSlimAppStrings, kFatBinaryFileName);
  616.     
  617.     // Get the same number of characters off the end of the filename
  618.     BlockMoveData(&(name[name[0] - searchStr[0] + 1]), &replaceStr[1], searchStr[0]);
  619.     replaceStr[0] = searchStr[0];
  620.     
  621.     // If they match, truncate the filename
  622.     if (EqualString(searchStr, replaceStr, false, false)) {
  623.         BlockMoveData(name, newName, name[0] + 1);
  624.         newName[0] -= searchStr[0];
  625.  
  626.         // Change the fileName (and ignore any error code since there's not
  627.         // much we can do about it anyway)
  628.         HRename(vRefNum, dirID, name, newName);
  629.         }
  630.  
  631.     // Get the current long version string
  632.     GetLongVersion(searchStr);
  633.     PtrToHand(&searchStr[1], &theStrHandle, searchStr[0]);
  634.     
  635.     // Get the "fat binary" string we want to search for
  636.     GetIndString(searchStr, kSlimAppStrings, kFatBinaryVersionName);
  637.     
  638.     // Get the right replacement string depending on which version we've now got
  639.     #ifdef powerc
  640.     GetIndString(replaceStr, kSlimAppStrings, kPowerMacVersionName);
  641.     #else
  642.     GetIndString(replaceStr, kSlimAppStrings, k68KVersionName);
  643.     #endif
  644.     
  645.     // ReplaceText() would be better since it handles any script system, but we
  646.     // want to remain compatible with System-6.
  647.     Munger(theStrHandle, 0L, &searchStr[1], searchStr[0], &replaceStr[1], replaceStr[0]);
  648.  
  649.     searchStr[0] = GetHandleSize(theStrHandle);
  650.     BlockMoveData(*theStrHandle, &searchStr[1], searchStr[0]);
  651.     SetLongVersion(searchStr);
  652.     
  653.     // If the application's name & info are currently visible in an open Finder window
  654.     // this will update the displayed info in the next few seconds.  Otherwise, it won't
  655.     // update until the folder is closed and reopened.  Note that the long version string
  656.     // that is displayed by the Finder's Get Info command unfortunately won't actually
  657.     // update until the user has quit the application.
  658.     ForceFinderToUpdateFolder(vRefNum, dirID);
  659.  
  660. }
  661.  
  662.  
  663.  
  664.  
  665. /*============================================================================
  666.   ForceFinderToUpdateFolder:
  667.  
  668.     This routine is adapted from a tip in MacTech Magazine.
  669.     It just gets the Finder to notice that we've changed some characteristics
  670.     of the file and update its folder if it happens to be currently displayed.
  671.  
  672.   ============================================================================*/
  673. void
  674. ForceFinderToUpdateFolder(short int vRefNum, short int dirID)
  675. {
  676.     CInfoPBRec tempPB;
  677.     
  678.     tempPB.dirInfo.ioNamePtr = nil;
  679.     tempPB.dirInfo.ioVRefNum = vRefNum;
  680.     tempPB.dirInfo.ioFDirIndex = -1;
  681.     tempPB.dirInfo.ioDrDirID = dirID;
  682.     
  683.     if (PBGetCatInfoSync(&tempPB) == noErr) {
  684.         tempPB.dirInfo.ioDrMdDat = LMGetTime();
  685.         tempPB.dirInfo.ioDrDirID = dirID;
  686.         PBSetCatInfoSync(&tempPB);
  687.         }
  688. }
  689.  
  690.  
  691.  
  692.  
  693.  
  694. /*============================================================================
  695.   ApplicationResourceFork:
  696.   
  697.     This function just returns the refNum for this application's (currently
  698.     open) resource fork.  The function GetAppParms() returns exactly what we
  699.     need, but it isn't defined by the Universal Headers if you're compiling
  700.     for a PowerPC.  Therefore, we actually need two versions of this function
  701.     if we want to remain System-6 compatible.
  702.  
  703.     For the PowerPC, they must be running System 7 or later, so we can be
  704.     certain that the Process Manager is available, and we can use it to get
  705.     an FSSpec for the application.  When we open the resource fork, we
  706.     actually just get the already open refNum, so there's
  707.     no need to close it later.
  708.     
  709.   ============================================================================*/
  710. short int
  711. GetApplicationResourceFork(void)
  712. {
  713.  
  714. #ifdef powerc
  715.  
  716.     ProcessSerialNumber psn;
  717.     ProcessInfoRec pInfo;
  718.     FSSpec theAppFile;
  719.     
  720.     GetCurrentProcess(&psn);
  721.     pInfo.processInfoLength = sizeof(ProcessInfoRec);
  722.     pInfo.processName = nil;
  723.     pInfo.processAppSpec = &theAppFile;
  724.     GetProcessInformation(&psn, &pInfo);
  725.     
  726.     // This should just return the refNum of the already open application
  727.     // resource fork.
  728.     return(FSpOpenResFile(&theAppFile, fsCurPerm));
  729.     
  730. #else
  731.  
  732.     Str255 appName;
  733.     Handle appFiles;
  734.     short int appResFork;
  735.     
  736.     GetAppParms(appName, &appResFork, &appFiles);
  737.  
  738.     return(appResFork);
  739.  
  740. #endif
  741. }
  742.  
  743.  
  744.  
  745.  
  746.  
  747. /*============================================================================
  748.   GetLongVersion:
  749.   
  750.     This routine retrieves the long version string from the 'vers' resource.
  751.     
  752.   ============================================================================*/
  753. void
  754. GetLongVersion(StringPtr versMsg)
  755. {
  756.     VersHandle versionInfo;
  757.     StringPtr versionStr;
  758.     short int curResFork;
  759.     
  760.     curResFork = CurResFile();
  761.     UseResFile(GetApplicationResourceFork());
  762.     
  763.     // Retrieve the resource
  764.     versionInfo = (VersHandle)Get1Resource('vers', 1);
  765.     UseResFile(curResFork);
  766.     if (versionInfo == nil) {
  767.         versMsg[0] = '\0';
  768.         return;
  769.         }
  770.  
  771.     // Take into account the variable length of the short version string that preceeds
  772.     // the long version string
  773.     versionStr = (StringPtr)&((*versionInfo)->versionNumber) + (*versionInfo)->versionNumber[0] + 1;
  774.  
  775.     // Extract the long version string
  776.     BlockMoveData(versionStr, versMsg, versionStr[0] + 1);
  777.     
  778.     // And we're done
  779.     ReleaseResource((Handle)versionInfo);
  780.     
  781. }
  782.  
  783.  
  784.  
  785.  
  786.  
  787. /*============================================================================
  788.   GetShortVersion:
  789.  
  790.     This routine retrieves the short version string from 'vers' resource .
  791.     
  792.   ============================================================================*/
  793. void
  794. GetShortVersion(StringPtr versMsg)
  795. {
  796.     VersHandle versionInfo;
  797.     short int curResFork;
  798.     
  799.     curResFork = CurResFile();
  800.     UseResFile(GetApplicationResourceFork());
  801.     
  802.     // Retrieve the resource
  803.     versionInfo = (VersHandle)Get1Resource('vers', 1);
  804.     UseResFile(curResFork);
  805.     if (versionInfo == nil) {
  806.         versMsg[0] = '\0';
  807.         return;
  808.         }
  809.  
  810.     // Extract the long version string
  811.     BlockMoveData((*versionInfo)->versionNumber, versMsg, (*versionInfo)->versionNumber[0] + 1);
  812.     
  813.     // And we're done
  814.     ReleaseResource((Handle)versionInfo);
  815. }
  816.  
  817.  
  818.  
  819.  
  820. /*============================================================================
  821.   SetLongVersion:
  822.  
  823.     This routine changes the long version string in 'vers' resource #1 to the
  824.     given string.
  825.  
  826.   ============================================================================*/
  827. OSErr
  828. SetLongVersion(StringPtr versionStr)
  829. {
  830.     OSErr err;
  831.     VersHandle versionInfo;
  832.     StringPtr currentVersion;
  833.     short int curResFork;
  834.     
  835.     // Make sure that we're using the application's 'vers' string
  836.     curResFork = CurResFile();
  837.     UseResFile(GetApplicationResourceFork());
  838.     
  839.     // Retrieve the resource
  840.     versionInfo = (VersHandle)Get1Resource('vers', 1);
  841.     if (versionInfo == nil) {
  842.         err = ResError();
  843.         UseResFile(curResFork);
  844.         return err;
  845.         }
  846.  
  847.     // Get a pointer to the current long version string, taking into account the
  848.     // variable length of the short version string that preceeds it
  849.     currentVersion = (StringPtr)&((*versionInfo)->versionNumber) +
  850.                                         (*versionInfo)->versionNumber[0] + 1;
  851.     
  852.     // Make enough room in the resource for the new version string (if we couldn't get enough, give up)
  853.     SetHandleSize((Handle)versionInfo,
  854.                     GetHandleSize((Handle)versionInfo) - currentVersion[0] + versionStr[0]);
  855.     if (MemError()) {
  856.         UseResFile(curResFork);
  857.         return MemError();
  858.         }
  859.  
  860.     // The resource handle might have moved, so get a pointer to the current long
  861.     // version string again
  862.     currentVersion = (StringPtr)&((*versionInfo)->versionNumber) +
  863.                                         (*versionInfo)->versionNumber[0] + 1;
  864.  
  865.     // And replace it with the new string
  866.     BlockMoveData(versionStr, currentVersion, versionStr[0] + 1);
  867.     ChangedResource((Handle)versionInfo);
  868.     WriteResource((Handle)versionInfo);
  869.     ReleaseResource((Handle)versionInfo);
  870.  
  871.     UseResFile(curResFork);
  872.  
  873.     return noErr;
  874. }
  875.  
  876.  
  877.  
  878.  
  879.